// WebSocket管理器单例类
class WebSocketManager {
  constructor() {
    if (WebSocketManager.instance) {
      return WebSocketManager.instance
    }
    
    // 初始化状态
    this.connected = false
    this.connecting = false // 连接中标记，防止重复连接
    this.initialized = false // 初始化标记，防止重复初始化
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
    this.messageHandlers = new Map() // 存储消息处理器
    this.roomSubscriptions = new Map() // 存储房间订阅
    this.reconnectTimer = null
    this.lastConnectTime = null
    this.lastDisconnectTime = null
    this.connectionPromises = [] // 存储等待连接的 Promise
    this.pendingRoomsData = new Map() // 存储等待处理的房间全量数据（在订阅之前收到）
    this.connectMessageSent = false // 标记是否已发送connect消息，避免重复发送
    
    // 绑定实例
    WebSocketManager.instance = this
    
    // 初始化WebSocket连接
    this.init()
  }
  
  // 获取单例实例
  static getInstance() {
    if (!WebSocketManager.instance) {
      WebSocketManager.instance = new WebSocketManager()
    }
    return WebSocketManager.instance
  }
  
  // 获取实例ID（用于验证单例）
  getInstanceId() {
    return this._instanceId || (this._instanceId = Math.random().toString(36).substr(2, 9))
  }
  
  // 获取连接统计信息
  getConnectionStats() {
    return {
      instanceId: this.getInstanceId(),
      connected: this.connected,
      reconnectAttempts: this.reconnectAttempts,
      messageHandlersCount: this.messageHandlers.size,
      roomSubscriptionsCount: this.roomSubscriptions.size,
      lastConnectTime: this.lastConnectTime,
      lastDisconnectTime: this.lastDisconnectTime
    }
  }
  
  // 获取连接状态
  getStatus() {
    return {
      connected: this.connected,
      reconnectAttempts: this.reconnectAttempts,
      maxReconnectAttempts: this.maxReconnectAttempts,
      hasReconnectTimer: !!this.reconnectTimer,
      messageHandlers: Array.from(this.messageHandlers.keys()),
      roomSubscriptions: Array.from(this.roomSubscriptions.keys())
    }
  }
  
  // 检查是否已连接
  isConnected() {
    return this.connected
  }
  
  // 确保连接状态（返回Promise，支持等待连接完成）
  ensureConnection() {
    return new Promise((resolve, reject) => {
      // 如果已连接，直接返回
      if (this.connected) {
        console.log('WebSocket已连接，直接返回')
        resolve(true)
        return
      }

      // 如果正在连接中，将 Promise 加入等待队列
      if (this.connecting) {
        console.log('WebSocket正在连接中，等待连接完成...')
        this.connectionPromises.push({ resolve, reject })
        return
      }

      // 如果还未初始化，先初始化（初始化会延迟2秒连接，我们需要立即连接）
      if (!this.initialized) {
        console.log('WebSocket未初始化，先初始化')
        this.init()
        // 等待初始化完成（监听器注册）
        setTimeout(() => {
          // 初始化完成后再执行连接逻辑
          this._executeConnection(resolve, reject)
        }, 100)
        return // 等待初始化完成后再继续
      }
      
      // 直接执行连接逻辑
      this._executeConnection(resolve, reject)
    })
  }
  
  // 执行连接逻辑（封装以便在初始化完成后调用）
  _executeConnection(resolve, reject) {
    // 设置连接完成的回调（使用超时避免无限等待）
    let resolved = false
    const timeout = setTimeout(() => {
        if (!resolved) {
          resolved = true
          this.connecting = false
          console.error('========== WebSocket连接超时 ==========')
          console.error('连接请求已发送，但15秒内未收到 onSocketOpen 事件')
          console.error('可能原因：')
          console.error('  1. 网络连接问题（防火墙、代理等）')
          console.error('  2. WebSocket服务器未正常运行')
          console.error('  3. Nginx配置问题（WebSocket代理未正确配置）')
          console.error('  4. SSL证书问题（wss连接）')
          console.error('尝试关闭现有连接并触发重连...')
          
          // 关闭现有连接对象（如果有）
          if (this.websocket) {
            try {
              wx.closeSocket()
            } catch (e) {
              console.warn('关闭超时连接时出错:', e)
            }
            this.websocket = null
          }
          
          // 解决所有等待连接的 Promise
          this.resolveConnectionPromises(false, new Error('连接超时'))
          reject(new Error('连接超时'))
          
          // 触发自动重连（如果还有重连次数）
          if (this.reconnectAttempts < this.maxReconnectAttempts) {
            console.log('连接超时，触发自动重连...')
            this.handleReconnect()
          } else {
            console.error('已达到最大重连次数，停止重连')
          }
        }
      }, 15000) // 15秒超时，给连接足够时间（包括网络延迟、服务器响应等）
      
      const onOpen = () => {
        if (!resolved) {
          resolved = true
          clearTimeout(timeout)
          this.connecting = false
          this.connected = true
          console.log('WebSocket连接已建立（ensureConnection）')
          this.resolveConnectionPromises(true, null)
          resolve(true)
        }
      }

      const onError = (err) => {
        if (!resolved) {
          resolved = true
          clearTimeout(timeout)
          this.connecting = false
          console.error('WebSocket连接失败（ensureConnection）', err)
          this.resolveConnectionPromises(false, err)
          reject(err)
        }
      }

      // 注册一次性监听器（虽然小程序环境不支持完全移除，但通过 resolved 标志避免重复处理）
      // 注意：init() 中已经注册了全局监听器，这里是补充监听（用于确保 ensureConnection 的 Promise 能正确解析）
      console.log('ensureConnection: 注册临时 onSocketOpen 监听器（补充监听）')
      wx.onSocketOpen(onOpen)
      wx.onSocketError(onError)

      // ⚠️ 重要：检查连接状态并决定是否调用 connect()
      // 必须先检查 websocket 状态，再设置 connecting 标志
      if (!this.websocket) {
        // 没有连接对象，开始连接
        console.log('立即开始WebSocket连接（ensureConnection）')
        // 标记为连接中（在调用 connect 之前）
        this.connecting = true
        this.connect()
      } else if (this.websocket && !this.connecting) {
        // 如果已有连接对象但未连接且未在连接中，可能是异常状态，重新连接
        console.log('检测到异常状态：有连接对象但未连接，重新连接')
        try {
          wx.closeSocket()
        } catch (e) {
          console.warn('关闭异常连接对象时出错:', e)
        }
        this.websocket = null
        this.connecting = true
        this.connect()
      } else if (this.websocket && this.connecting) {
        // 如果已有连接对象且正在连接中，等待连接完成（已加入队列）
        console.log('已有连接对象且正在连接中，等待连接完成')
        console.log('当前 websocket 对象:', !!this.websocket)
        console.log('当前 connecting 状态:', this.connecting)
        console.log('当前 connected 状态:', this.connected)
        // 不需要再次调用 connect()，等待现有连接完成即可
        // 注意：Promise 会通过 onSocketOpen 回调中的 resolveConnectionPromises 解决
        // 但如果连接一直未完成，我们需要确保 Promise 最终会被解决
        // 这里我们依赖超时机制或 onSocketOpen 事件来解决 Promise
      } else {
        // 其他异常状态
        console.warn('检测到异常连接状态，重置并重新连接')
        this.websocket = null
        this.connecting = true
        this.connect()
      }
  }
  
  // 解决所有等待连接的 Promise
  resolveConnectionPromises(success, error) {
    const promises = this.connectionPromises
    this.connectionPromises = []
    
    promises.forEach(({ resolve, reject }) => {
      if (success) {
        resolve(true)
      } else {
        reject(error || new Error('连接失败'))
      }
    })
  }
  
  // 等待连接完成（同步版本，返回布尔值）
  waitForConnection(maxWait = 5000) {
    if (this.connected) {
      return true
    }
    
    const startTime = Date.now()
    while (!this.connected && (Date.now() - startTime) < maxWait) {
      // 等待连接，小程序环境使用简单的延迟
      // 注意：这是同步等待，可能阻塞，建议使用 ensureConnection
    }
    
    return this.connected
  }
  
  // 初始化WebSocket
  init() {
    // 防止重复初始化
    if (this.initialized) {
      console.log('WebSocket管理器已初始化，跳过重复初始化')
      return
    }
    
    console.log('========== 初始化WebSocket管理器单例 ==========')
    this.initialized = true
    
    // ⚠️ 重要：小程序的 wx.onSocketOpen 必须在 wx.connectSocket 调用之前注册！
    // 如果先调用 connectSocket，后注册 onSocketOpen，可能会错过事件
    // 所以我们要在初始化时就注册所有事件监听器
    console.log('注册 WebSocket 事件监听器（必须在 connectSocket 之前注册）')
    
    // 设置全局WebSocket事件监听器（这些监听器是全局的，只会注册一次）
    // 注意：小程序的 wx.onSocketOpen 可以多次调用，会注册多个监听器
    // 所有监听器都会在连接打开时触发，所以我们需要确保逻辑的幂等性
    wx.onSocketOpen(() => {
      console.log('========== WebSocket连接已打开 ==========')
      console.log('连接时间:', new Date().toLocaleString())
      console.log('当前 connected 状态:', this.connected)
      console.log('当前 connecting 状态:', this.connecting)
      
      // 防止重复处理（如果已经连接，跳过）
      if (this.connected) {
        console.log('⚠️ 警告：连接已打开，但 connected 状态已经是 true，可能重复触发了 onSocketOpen')
        console.log('继续执行 connect 消息发送逻辑...')
      }
      
      this.connected = true
      this.connecting = false // 清除连接中标记
      this.reconnectAttempts = 0
      this.lastConnectTime = Date.now()
      
      // 解决所有等待连接的 Promise
      this.resolveConnectionPromises(true, null)
      
      // 连接建立后，立即发送用户ID给后端（不延迟，避免连接被关闭）
      // 因为WebSocket只会在用户登录后才初始化
      const sendConnectMessage = () => {
        // 检查连接状态
        if (!this.connected) {
          console.error('❌ 尝试发送connect消息时，连接已断开')
          return
        }
        
        const userId = this.getCurrentUserId()
        console.log('========== 准备发送connect消息 ==========')
        console.log('WebSocket连接状态:', this.connected)
        console.log('用户ID:', userId)
        console.log('用户ID类型:', typeof userId)
        console.log('globalData.userInfo:', getApp().globalData.userInfo)
        console.log('storage userInfo:', wx.getStorageSync('userInfo'))
        
        if (!userId) {
          console.warn('⚠️ WebSocket连接建立，但无法获取用户ID，将持续重试')
          console.warn('这可能表示用户信息还未准备好，继续重试...')
          
          // 延迟重试获取用户ID（增加重试次数和间隔，确保用户信息有足够时间准备）
          let retryCount = 0
          const maxRetries = 10 // 增加到10次
          const retry = () => {
            if (retryCount >= maxRetries) {
              console.error('❌ 重试次数已达上限，无法获取用户ID')
              console.error('建议检查：1. 用户是否已登录 2. userInfo是否正确保存到storage')
              return
            }
            
            // 检查连接状态
            if (!this.connected) {
              console.error('❌ 重试时连接已断开')
              return
            }
            
            retryCount++
            
            // 增加延迟时间，让用户信息有足够时间准备（前3次快速重试，之后逐渐增加）
            const delay = retryCount <= 3 ? 500 : retryCount <= 6 ? 1000 : 2000
            
            setTimeout(() => {
              if (!this.connected) {
                console.error('❌ 重试时连接已断开')
                return
              }
              
              // 重新获取用户信息（尝试从多个来源）
              const app = getApp()
              let retryUserId = null
              
              // 方式1：从globalData获取
              if (app.globalData.userInfo?.id) {
                retryUserId = app.globalData.userInfo.id
              }
              
              // 方式2：从storage获取
              if (!retryUserId) {
                const userInfo = wx.getStorageSync('userInfo')
                if (userInfo?.id) {
                  retryUserId = userInfo.id
                  // 同步到globalData
                  if (!app.globalData.userInfo) {
                    app.globalData.userInfo = userInfo
                  }
                }
              }
              
              if (retryUserId) {
                console.log(`✅ 延迟重试获取用户ID成功 (第${retryCount}次):`, retryUserId)
                const messageData = { user_id: retryUserId }
                const sendResult = this.sendMessage('connect', messageData)
                if (sendResult) {
                  this.connectMessageSent = true
                  console.log('✅ connect消息发送成功')
                } else {
                  console.warn('⚠️ connect消息发送失败，继续重试...')
                  retry()
                }
              } else {
                console.warn(`⚠️ 第${retryCount}次重试仍无法获取用户ID，${delay}ms后继续重试...`)
                console.warn('当前globalData.userInfo:', app.globalData.userInfo)
                console.warn('当前storage userInfo:', wx.getStorageSync('userInfo'))
                retry()
              }
            }, delay)
          }
          retry()
          return
        }
        
        console.log('✅ 用户ID有效，立即发送connect消息，用户ID:', userId)
        const messageData = {
          user_id: userId
        }
        console.log('connect消息数据:', messageData)
        
        const sendResult = this.sendMessage('connect', messageData)
        console.log('connect 消息发送结果:', sendResult)
        
        if (!sendResult) {
          console.warn('⚠️ connect 消息发送失败（返回false），1秒后重试')
          // 如果发送失败，延迟重试
          if (this.connected) {
            setTimeout(() => {
              if (this.connected) {
                const retryUserId = this.getCurrentUserId()
                if (retryUserId) {
                  console.log('🔄 重试发送 connect 消息，用户ID:', retryUserId)
                  const retryResult = this.sendMessage('connect', {
                    user_id: retryUserId
                  })
                  if (retryResult) {
                    this.connectMessageSent = true
                  }
                } else {
                  console.error('❌ 重试时无法获取用户ID')
                }
              } else {
                console.error('❌ 重试时WebSocket已断开')
              }
            }, 1000)
          }
        } else {
          console.log('✅ connect 消息已发送，等待后端响应...')
          this.connectMessageSent = true
        }
      }
      
      // 立即发送，不延迟（避免连接被关闭）
      // 使用 setTimeout(fn, 0) 确保在当前执行栈完成后立即执行
      setTimeout(sendConnectMessage, 0)
      
      // 备用检查：如果1秒后还没有发送 connect 消息，强制发送
      // 这可以应对 onSocketOpen 触发但 sendConnectMessage 执行失败的情况
      setTimeout(() => {
        if (this.connected && !this.connectMessageSent) {
          console.warn('⚠️ 警告：连接已建立1秒，但 connect 消息仍未发送，尝试强制发送...')
          this.ensureConnectMessageSent()
        }
      }, 1000)
      
      // 再次检查：如果3秒后还没有发送，再次尝试
      setTimeout(() => {
        if (this.connected && !this.connectMessageSent) {
          console.warn('⚠️ 警告：连接已建立3秒，但 connect 消息仍未发送，最后一次尝试...')
          const userId = this.getCurrentUserId()
          if (userId) {
            console.log('找到用户ID，强制发送 connect 消息:', userId)
            const result = this.sendMessage('connect', { user_id: userId })
            if (result) {
              this.connectMessageSent = true
              console.log('✅ 强制发送 connect 消息成功')
            } else {
              console.error('❌ 强制发送 connect 消息失败')
            }
          } else {
            console.error('❌ 强制发送时仍无法获取用户ID')
          }
        }
      }, 3000)
    })
    
    // ⚠️ 注意：这是 init() 中注册的全局监听器
    // 但我们也会在每次连接时（_doConnect）注册新的监听器
    // 这样可以确保监听器是最新的，不会被之前的连接状态影响

    wx.onSocketMessage((res) => {
      try {
        console.log('========== 收到WebSocket服务器消息 ==========')
        console.log('原始消息:', res.data)
        console.log('消息长度:', res.data?.length || 0)
        const message = JSON.parse(res.data)
        console.log('解析后的消息:', message)
        console.log('消息类型:', message.type)
        if (message.type === 'connect_success') {
          console.log('✅ 连接成功，已订阅房间:', message.subscribed_rooms)
          console.log('房间数据:', message.rooms_data)
        }
        this.handleMessage(message)
      } catch (e) {
        console.error('❌ 解析WebSocket消息失败:', e)
        console.error('原始数据:', res.data)
        console.error('错误堆栈:', e.stack)
      }
    })

    wx.onSocketError((err) => {
      console.error('========== WebSocket连接错误 ==========')
      console.error('错误信息:', err)
      console.error('错误详情:', JSON.stringify(err))
      console.error('错误类型:', typeof err)
      console.error('可能原因:')
      console.error('  1. WebSocket 服务器未运行或无法访问')
      console.error('  2. Nginx 代理配置问题（WebSocket 升级失败）')
      console.error('  3. SSL/TLS 握手失败')
      console.error('  4. 防火墙或网络问题')
      console.error('  5. URL 配置错误')
      this.connected = false
      this.connecting = false // 清除连接中标记
      this.lastDisconnectTime = Date.now()
      // 解决所有等待连接的 Promise（失败）
      this.resolveConnectionPromises(false, err)
      this.handleReconnect()
    })

    wx.onSocketClose((res) => {
      console.log('========== WebSocket连接关闭 ==========')
      console.log('关闭代码:', res.code)
      console.log('关闭原因:', res.reason)
      console.log('是否为正常关闭:', res.wasClean)
      
      // 分析关闭代码的含义
      const codeMessages = {
        1000: '正常关闭（CLOSE_NORMAL）',
        1001: '端点离开（CLOSE_GOING_AWAY）',
        1002: '协议错误（CLOSE_PROTOCOL_ERROR）',
        1003: '数据类型错误（CLOSE_UNSUPPORTED）',
        1006: '异常关闭（CLOSE_ABNORMAL）- 没有收到关闭帧',
        1007: '数据格式错误（CLOSE_INVALID_PAYLOAD）',
        1008: '策略违规（CLOSE_POLICY_VIOLATION）',
        1009: '消息太大（CLOSE_TOO_LARGE）',
        1010: '扩展协商失败（CLOSE_MANDATORY_EXTENSION）',
        1011: '服务器内部错误（CLOSE_INTERNAL_SERVER_ERROR）',
        1015: 'TLS 握手失败（CLOSE_TLS_HANDSHAKE）'
      }
      
      const codeMessage = codeMessages[res.code] || '未知关闭代码'
      console.log('关闭代码含义:', codeMessage)
      
      if (res.code === 1006) {
        console.error('❌ 异常关闭（1006）可能原因:')
        console.error('  1. WebSocket 握手失败（最常见）')
        console.error('  2. 服务器主动断开连接')
        console.error('  3. 网络中断')
        console.error('  4. Nginx 代理配置问题')
        console.error('  5. SSL/TLS 握手失败')
        console.error('建议检查:')
        console.error('  - Nginx 配置中的 proxy_http_version 和 upgrade 头')
        console.error('  - 后端 WebSocket 服务器是否正常运行')
        console.error('  - 网络连接是否正常')
      }
      
      this.connected = false
      this.connecting = false // 清除连接中标记
      this.connectMessageSent = false
      this.lastDisconnectTime = Date.now()
      // 解决所有等待连接的 Promise（失败）
      this.resolveConnectionPromises(false, new Error('WebSocket连接已关闭'))
      this.handleReconnect()
    })
    
    // 延迟连接（如果还没有连接请求）- 仅在用户登录后才连接
    // 如果 ensureConnection 被调用，它会立即触发连接，所以这里延迟连接作为备用
    // 注意：延迟连接只是备用机制，不应该与 ensureConnection 冲突
    setTimeout(() => {
      // 如果还没有连接且没有正在连接，且用户已登录，才执行延迟连接
      // 这样可以避免与 ensureConnection 的立即连接冲突
      const userInfo = wx.getStorageSync('userInfo') || getApp().globalData.userInfo
      if (!this.connected && !this.connecting && !this.websocket && userInfo) {
        console.log('延迟连接触发（备用连接）- 用户已登录')
        this.connect()
      } else {
        if (this.connected || this.connecting || this.websocket) {
          console.log('延迟连接跳过（已有连接或正在连接）')
        } else {
          console.log('延迟连接跳过（用户未登录）')
        }
      }
    }, 2000)
  }
  
  // 连接WebSocket
  connect() {
    if (this.connected) {
      console.log('WebSocket已连接，跳过重复连接')
      return
    }
    
    // 如果正在连接中，避免重复连接
    if (this.connecting) {
      console.log('WebSocket正在连接中，跳过重复连接请求')
      return
    }

    // ⚠️ 关键检查：确保在调用 connectSocket 之前，onSocketOpen 监听器已经注册
    // 小程序的 onSocketOpen 必须在 connectSocket 之前注册，否则可能错过事件
    if (!this.initialized) {
      console.warn('⚠️ 警告：连接前未初始化，先初始化以注册事件监听器')
      this.init()
      // 短暂延迟确保监听器注册完成（虽然理论上同步应该可以）
      setTimeout(() => {
        if (!this.connected && !this.connecting) {
          this._doConnect()
        }
      }, 50)
      return
    }

    this._doConnect()
  }
  
  // 实际执行连接的内部方法（确保监听器已注册后才调用）
  _doConnect() {
    console.log('========== 开始连接WebSocket ==========')
    console.log('⚠️ 关键步骤：在 connectSocket 调用之前立即注册 onSocketOpen 监听器')
    console.log('这确保监听器注册在连接请求发送的同一事件循环中')
    
    // ⚠️ 关键修复：在调用 connectSocket 之前，立即注册 onSocketOpen 监听器
    // 小程序的 wx.onSocketOpen 必须在 wx.connectSocket 之前注册
    // 即使之前已经在 init() 中注册过，我们也要在这里再次注册，确保在当前连接尝试时监听器是活跃的
    // 小程序允许多次注册，所有监听器都会触发，这是安全的
    
    // 注册主监听器（立即执行，不延迟）
    const mainOnOpenHandler = () => {
      console.log('========== [主监听器] WebSocket连接已打开 ==========')
      console.log('连接时间:', new Date().toLocaleString())
      console.log('当前 connected 状态:', this.connected)
      console.log('当前 connecting 状态:', this.connecting)
      
      // 防止重复处理
      if (this.connected) {
        console.log('⚠️ 警告：连接已打开，但 connected 状态已经是 true，可能重复触发')
        return
      }
      
      this.connected = true
      this.connecting = false
      this.reconnectAttempts = 0
      this.lastConnectTime = Date.now()
      
      // 解决所有等待连接的 Promise
      this.resolveConnectionPromises(true, null)
      
      // 立即发送 connect 消息（使用内联函数避免方法定义问题）
      this._sendConnectMessageAfterOpen()
    }
    
    // ⚠️ 关键：立即注册监听器（必须在 connectSocket 之前）
    // 每次连接尝试都要重新注册，确保监听器是活跃的
    wx.onSocketOpen(mainOnOpenHandler)
    console.log('✅ 主 onSocketOpen 监听器已注册（_doConnect，在 connectSocket 之前）')
    
    // 额外注册一个备用监听器（双重保险）
    const backupOnOpenHandler = () => {
      console.log('========== [备用监听器] WebSocket连接已打开（备用监听器） ==========')
      // 如果主监听器没触发，这个备用监听器会处理
      if (!this.connected) {
        console.log('⚠️ 主监听器未触发，备用监听器接管处理')
        mainOnOpenHandler()
      }
    }
    wx.onSocketOpen(backupOnOpenHandler)
    console.log('✅ 备用 onSocketOpen 监听器已注册（_doConnect，双重保险）')
    
    // 标记为连接中（在 connectSocket 之前）
    this.connecting = true

    try {
      const appConfig = require('../config/app.js')
      console.log('连接地址:', appConfig.server.websocketUrl)
      console.log('当前时间:', new Date().toLocaleString())
      console.log('✅ 确认：onSocketOpen 监听器已注册（init() 已完成 + _doConnect 新注册）')
      console.log('⚠️ 如果 onSocketOpen 未触发，将在3秒后使用备用检测机制')
      
      // 记录连接请求发送的时间
      const connectStartTime = Date.now()
      
      this.websocket = wx.connectSocket({
        url: appConfig.server.websocketUrl,
        success: () => {
          const requestSendTime = Date.now() - connectStartTime
          console.log('✅ WebSocket连接请求已发送（耗时 ' + requestSendTime + 'ms）')
          console.log('等待服务器响应和 onSocketOpen 事件触发...')
          console.log('⚠️ 如果 onSocketOpen 未在10秒内触发，将使用备用检测机制')
          
          // 备用检测：3秒后检查 onSocketOpen 是否触发
          setTimeout(() => {
            if (!this.connected && this.connecting) {
              console.warn('⚠️ 警告：连接请求发送3秒后，onSocketOpen 仍未触发')
              console.warn('尝试使用 sendSocketMessage 检测连接状态...')
              
              try {
                // 尝试发送测试消息来检测连接是否真的建立
                const testMessage = JSON.stringify({ type: 'ping', timestamp: Date.now() })
                wx.sendSocketMessage({
                  data: testMessage,
                  success: () => {
                    // 发送成功，说明连接已建立，但 onSocketOpen 没触发
                    console.log('✅ 检测到连接已建立（通过发送测试消息），但 onSocketOpen 未触发')
                    console.log('⚠️ 这是小程序事件系统的已知问题，手动触发连接成功逻辑...')
                    
                    // 手动触发连接成功的逻辑
                    this.connected = true
                    this.connecting = false
                    this.reconnectAttempts = 0
                    this.lastConnectTime = Date.now()
                    this.resolveConnectionPromises(true, null)
                    
                    // 发送 connect 消息
                    this._sendConnectMessageAfterOpen()
                  },
                  fail: (err) => {
                    console.log('检测结果：连接未建立（发送失败）', err)
                    console.log('继续等待 onSocketOpen 事件...')
                  }
                })
              } catch (err) {
                console.log('检测连接状态时出错:', err)
              }
            }
          }, 3000)
          
          // 10秒超时检测
          setTimeout(() => {
            if (!this.connected && this.connecting) {
              console.error('❌ 错误：连接请求发送10秒后，onSocketOpen 仍未触发且连接检测失败')
              console.error('判断为连接失败，将触发重连机制')
              
              this.connecting = false
              try {
                wx.closeSocket()
              } catch (e) {
                console.warn('关闭失败连接时出错:', e)
              }
              this.websocket = null
              this.resolveConnectionPromises(false, new Error('连接超时：onSocketOpen 未触发'))
              
              if (this.reconnectAttempts < this.maxReconnectAttempts) {
                console.log('触发自动重连...')
                this.handleReconnect()
              }
            }
          }, 10000)
        },
        fail: (err) => {
          console.error('❌ WebSocket连接请求失败:', err)
          this.connecting = false
          this.handleReconnect()
        }
      })
    } catch (error) {
      console.error('❌ WebSocket连接异常（_doConnect）:', error)
      this.connecting = false
      this.handleReconnect()
    }
  }
  
  // 发送 connect 消息的辅助方法（从 onSocketOpen 调用）
  _sendConnectMessageAfterOpen() {
    const sendConnectMessage = () => {
        // 检查连接状态
        if (!this.connected) {
          console.error('❌ 尝试发送connect消息时，连接已断开')
          return
        }
        
        const userId = this.getCurrentUserId()
        console.log('========== 准备发送connect消息 ==========')
        console.log('WebSocket连接状态:', this.connected)
        console.log('用户ID:', userId)
        
        if (!userId) {
          console.warn('⚠️ WebSocket连接建立，但无法获取用户ID，将持续重试')
          // 延迟重试获取用户ID
          let retryCount = 0
          const maxRetries = 10
          const retry = () => {
            if (retryCount >= maxRetries || !this.connected) {
              console.error('❌ 重试次数已达上限或连接已断开，无法获取用户ID')
              return
            }
            
            retryCount++
            const delay = retryCount <= 3 ? 500 : retryCount <= 6 ? 1000 : 2000
            
            setTimeout(() => {
              if (!this.connected) return
              
              const app = getApp()
              let retryUserId = app.globalData.userInfo?.id || wx.getStorageSync('userInfo')?.id
              
              if (retryUserId) {
                console.log(`✅ 延迟重试获取用户ID成功 (第${retryCount}次):`, retryUserId)
                const sendResult = this.sendMessage('connect', { user_id: retryUserId })
                if (sendResult) {
                  this.connectMessageSent = true
                  console.log('✅ connect消息发送成功')
                } else {
                  retry()
                }
              } else {
                retry()
              }
            }, delay)
          }
          retry()
          return
        }
        
        console.log('✅ 用户ID有效，立即发送connect消息，用户ID:', userId)
        const sendResult = this.sendMessage('connect', { user_id: userId })
        
        if (sendResult) {
          console.log('✅ connect 消息已发送，等待后端响应...')
          this.connectMessageSent = true
        } else {
          console.warn('⚠️ connect 消息发送失败（返回false），1秒后重试')
          if (this.connected) {
            setTimeout(() => {
              if (this.connected) {
                const retryUserId = this.getCurrentUserId()
                if (retryUserId) {
                  const retryResult = this.sendMessage('connect', { user_id: retryUserId })
                  if (retryResult) {
                    this.connectMessageSent = true
                  }
                }
              }
            }, 1000)
          }
        }
      }
      
      // 立即发送，不延迟
      setTimeout(sendConnectMessage, 0)
      
      // 备用检查：如果1秒后还没有发送，强制发送
      setTimeout(() => {
        if (this.connected && !this.connectMessageSent) {
          console.warn('⚠️ 警告：连接已建立1秒，但 connect 消息仍未发送，尝试强制发送...')
          this.ensureConnectMessageSent()
        }
      }, 1000)
      
      // 再次检查：如果3秒后还没有发送，再次尝试
      setTimeout(() => {
        if (this.connected && !this.connectMessageSent) {
          console.warn('⚠️ 警告：连接已建立3秒，但 connect 消息仍未发送，最后一次尝试...')
          const userId = this.getCurrentUserId()
          if (userId) {
            const result = this.sendMessage('connect', { user_id: userId })
            if (result) {
              this.connectMessageSent = true
              console.log('✅ 强制发送 connect 消息成功')
            }
          }
        }
      }, 3000)
  }
  
  // 处理重连
  handleReconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.log('WebSocket重连次数已达上限，停止重连')
      return
    }

    this.reconnectAttempts++
    const delay = 3000 * this.reconnectAttempts
    
    console.log(`WebSocket重连尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts}，${delay}ms后重连`)
    
    this.reconnectTimer = setTimeout(() => {
      this.connect()
    }, delay)
  }
  
  // 强制重连
  forceReconnect() {
    console.log('强制重新连接WebSocket')
    this.connected = false
    this.connecting = false // 清除连接中标记
    this.reconnectAttempts = 0
    this.connectMessageSent = false // 重要：重置 connect 消息发送标志，确保重连后重新发送
    
    // 清空所有等待连接的 Promise
    this.resolveConnectionPromises(false, new Error('强制重连'))
    
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }
    
    // 关闭现有连接（如果有）
    if (this.websocket) {
      try {
        wx.closeSocket()
      } catch (e) {
        console.warn('关闭现有连接失败:', e)
      }
      this.websocket = null
    }
    
    // 重连（连接成功后会自动发送 connect 消息）
    this.connect()
    
    // 等待连接建立后，确保发送 connect 消息（双重保险）
    // 因为 onSocketOpen 中已经有自动发送逻辑，这里作为备用检查
    setTimeout(() => {
      if (this.connected && !this.connectMessageSent) {
        console.log('🔄 重连后检查：连接已建立但未发送connect消息，立即发送')
        this.ensureConnectMessageSent()
      }
    }, 2000) // 等待2秒，让连接有足够时间建立
  }
  
  // 检查连接状态，如果断开则重连（用于 onShow 时调用）
  checkAndReconnect() {
    if (this.connected) {
      console.log('WebSocket 已连接，无需重连')
      return Promise.resolve(true)
    }
    
    console.log('WebSocket 未连接，开始重连...')
    return this.ensureConnection()
  }
  
  // 检查连接状态
  isConnected() {
    return this.connected
  }
  
  // 确保已发送 connect 消息（用于页面加载时检查）
  ensureConnectMessageSent() {
    // 检查连接状态（即使 connected 为 false，也可能实际已连接）
    // 小程序没有直接的 readyState，我们通过尝试发送消息来判断
    if (!this.websocket) {
      console.log('WebSocket对象不存在，跳过发送connect消息')
      return false
    }
    
    // 如果 connected 为 false，但我们有 websocket 对象，尝试手动设置为已连接
    // 这可能是 onSocketOpen 没有触发的情况
    if (!this.connected && this.websocket) {
      console.log('⚠️ 检测到 websocket 对象存在但 connected 为 false')
      console.log('可能是 onSocketOpen 事件未触发，尝试手动设置连接状态')
      console.log('注意：这可能导致状态不一致，但如果连接实际已建立，可以继续')
      
      // 尝试发送测试消息来判断连接是否真的建立了
      // 由于小程序没有直接的 readyState，我们保守处理：不自动设置 connected
      // 而是让用户等待 onSocketOpen 事件
      console.log('建议：等待 onSocketOpen 事件触发，或检查网络连接')
      return false
    }
    
    // 如果未连接，不需要发送
    if (!this.connected) {
      console.log('WebSocket未连接，跳过发送connect消息')
      return false
    }
    
    // 如果已发送，不需要重复发送
    if (this.connectMessageSent) {
      console.log('connect消息已发送，跳过重复发送')
      return true
    }
    
    // 检查是否有用户ID
    const userId = this.getCurrentUserId()
    if (!userId) {
      console.warn('⚠️ 无法获取用户ID，无法发送connect消息')
      console.warn('globalData.userInfo:', getApp().globalData.userInfo)
      console.warn('storage userInfo:', wx.getStorageSync('userInfo'))
      return false
    }
    
    console.log('🔄 检测到连接已建立但未发送connect消息，立即发送，用户ID:', userId)
    const messageData = { user_id: userId }
    const sendResult = this.sendMessage('connect', messageData)
    
    if (sendResult) {
      this.connectMessageSent = true
      console.log('✅ connect消息发送成功')
      return true
    } else {
      console.warn('⚠️ connect消息发送失败')
      return false
    }
  }
  
  // 获取连接状态
  getStatus() {
    return {
      connected: this.connected,
      reconnectAttempts: this.reconnectAttempts,
      maxReconnectAttempts: this.maxReconnectAttempts,
      canReconnect: this.reconnectAttempts < this.maxReconnectAttempts
    }
  }
  
  // 注册消息处理器
  registerMessageHandler(messageType, handler) {
    if (!this.messageHandlers.has(messageType)) {
      this.messageHandlers.set(messageType, [])
    }
    this.messageHandlers.get(messageType).push(handler)
    console.log(`注册消息处理器: ${messageType}`)
  }
  
  // 注销消息处理器
  unregisterMessageHandler(messageType, handler) {
    if (this.messageHandlers.has(messageType)) {
      const handlers = this.messageHandlers.get(messageType)
      const index = handlers.indexOf(handler)
      if (index > -1) {
        handlers.splice(index, 1)
        console.log(`注销消息处理器: ${messageType}`)
      }
    }
  }
  
  // 注册房间消息处理器（前端只负责注册处理器，订阅由后端自动完成）
  registerRoomHandler(roomId, handler) {
    this.roomSubscriptions.set(roomId, handler)
    console.log(`注册房间消息处理器: ${roomId}`)
    
    // 如果已有该房间的待处理全量数据，立即触发刷新
    if (this.pendingRoomsData.has(roomId)) {
      console.log(`房间 ${roomId} 已有待处理的全量数据，立即触发刷新`)
      try {
        handler(this.pendingRoomsData.get(roomId))
        // 处理完后移除
        this.pendingRoomsData.delete(roomId)
      } catch (error) {
        console.error(`房间 ${roomId} 全量刷新处理失败:`, error)
      }
    }
  }
  
  // 移除房间消息处理器
  unregisterRoomHandler(roomId) {
    this.roomSubscriptions.delete(roomId)
    console.log(`移除房间消息处理器: ${roomId}`)
  }
  
  // 订阅房间消息（保留用于向后兼容，实际订阅由后端自动完成）
  async subscribeRoom(roomId, handler) {
    // 只注册处理器，不发送订阅消息（后端已在 connect 时自动订阅）
    this.registerRoomHandler(roomId, handler)
    console.log(`注册房间处理器: ${roomId}（后端会自动订阅）`)
    return true
  }
  
  // 取消订阅房间消息
  unsubscribeRoom(roomId) {
    this.roomSubscriptions.delete(roomId)
    console.log(`取消订阅房间消息: ${roomId}`)
    
    // 如果已连接，发送离开房间消息
    if (this.connected) {
      this.sendMessage('leave_room', {
        room_id: roomId,
        user_id: this.getCurrentUserId()
      })
    }
  }
  
  // 发送消息
  sendMessage(type, data) {
    if (!this.connected) {
      console.warn('WebSocket未连接，无法发送消息，类型:', type)
      return false
    }

    try {
      const message = {
        type: type,
        ...data,
        timestamp: Date.now()
      }
      
      const messageStr = JSON.stringify(message)
      console.log('========== 准备发送WebSocket消息 ==========')
      console.log('消息类型:', type)
      console.log('消息数据:', data)
      console.log('完整消息:', messageStr)
      console.log('当前连接状态:', this.connected)
      
      wx.sendSocketMessage({
        data: messageStr,
        success() {
          console.log('✅ WebSocket消息发送成功:', type)
          if (type === 'connect') {
            console.log('connect消息已发送，等待后端响应...')
          }
        },
        fail(err) {
          console.error('❌ WebSocket消息发送失败:', type, err)
        }
      })
      return true
    } catch (error) {
      console.error('发送WebSocket消息异常:', error)
      return false
    }
  }
  
  // 处理消息
  handleMessage(message) {
    console.log('WebSocketManager: 收到消息:', message)
    
    // ⚠️ 处理嵌套消息结构：如果消息包含嵌套的 message 对象，提取内部消息
    // 格式：{ room_id: 204, message: { type: "scoreUpdated", ... }, timestamp: ... }
    let actualMessage = message
    let actualType = message.type
    
    if (message.message && typeof message.message === 'object') {
      // 这是一个嵌套消息结构，使用内部的 message 对象
      console.log('检测到嵌套消息结构，使用内部 message 对象')
      actualMessage = {
        ...message.message,  // 内部消息的所有字段
        room_id: message.room_id || message.message.room_id,  // 保留 room_id（可能在外部或内部）
        timestamp: message.timestamp || message.message.timestamp  // 保留 timestamp
      }
      actualType = actualMessage.type || message.type
      console.log('提取的实际消息:', actualMessage)
      console.log('实际消息类型:', actualType)
    }
    
    // 处理连接成功消息（后端返回已订阅的房间列表和每个房间的完整状态）
    if (actualType === 'connect_success') {
      console.log('WebSocket连接成功，已订阅的房间:', message.subscribed_rooms)
      console.log('收到房间全量数据:', message.rooms_data)
      
      // 如果有房间全量数据，触发全量刷新
      if (message.rooms_data && typeof message.rooms_data === 'object') {
        console.log('触发全量刷新，房间数量:', Object.keys(message.rooms_data).length)
        
        // 先存储全量数据，即使房间处理器还未注册
        Object.keys(message.rooms_data).forEach(roomId => {
          const roomData = message.rooms_data[roomId]
          this.pendingRoomsData.set(roomId, {
            type: 'room_full_refresh',
            room_id: parseInt(roomId),
            room_info: {
              id: roomData.room_id,
              room_code: roomData.room_code,
              name: roomData.name,
              status: roomData.status,
              current_players: roomData.current_players,
              max_players: roomData.max_players,
              created_by: roomData.created_by
            },
            users: roomData.users || [],
            timestamp: roomData.updated_at || Date.now()
          })
        })
        
        // 通知所有已注册的房间处理器进行全量刷新
        Object.keys(message.rooms_data).forEach(roomId => {
          const roomData = message.rooms_data[roomId]
          if (this.roomSubscriptions.has(roomId)) {
            const handler = this.roomSubscriptions.get(roomId)
            try {
              // 发送全量刷新消息给房间处理器
              handler(this.pendingRoomsData.get(roomId))
              // 处理完后移除
              this.pendingRoomsData.delete(roomId)
            } catch (error) {
              console.error(`房间 ${roomId} 全量刷新处理失败:`, error)
            }
          }
        })
      }
      
      // 可以在这里触发连接成功的回调
      if (this.onConnectSuccess) {
        this.onConnectSuccess(message)
      }
    }
    
    // 调用注册的消息处理器（使用实际消息类型）
    if (actualType && this.messageHandlers.has(actualType)) {
      const handlers = this.messageHandlers.get(actualType)
      handlers.forEach(handler => {
        try {
          handler(actualMessage)
        } catch (error) {
          console.error('消息处理器执行失败:', error)
        }
      })
    }
    
    // 处理房间消息（根据 room_id 分发）
    // 优先使用提取后的消息，如果没有 room_id，使用原始消息
    const roomId = actualMessage.room_id || message.room_id
    if (roomId && this.roomSubscriptions.has(roomId)) {
      const handler = this.roomSubscriptions.get(roomId)
      try {
        // 传递完整的实际消息给房间处理器
        handler(actualMessage)
        console.log(`✅ 房间消息已分发到房间 ${roomId} 的处理器，消息类型: ${actualType}`)
      } catch (error) {
        console.error(`❌ 房间 ${roomId} 消息处理器执行失败:`, error)
        console.error('错误堆栈:', error.stack)
      }
    } else if (roomId) {
      console.warn(`⚠️ 房间 ${roomId} 的消息已收到，但未注册房间处理器，消息类型: ${actualType}`)
      console.warn('已注册的房间处理器:', Array.from(this.roomSubscriptions.keys()))
    }
    
    // 如果没有特定房间ID，也可以处理通用消息（如用户加入、离开等）
    // 这些消息可能包含在 message.data 或其他字段中
  }
  
  // 获取当前用户ID
  getCurrentUserId() {
    const app = getApp()
    
    // 优先从全局数据获取
    if (app.globalData.userInfo?.id) {
      return app.globalData.userInfo.id
    }
    
    // 从本地存储获取
    const userInfo = wx.getStorageSync('userInfo')
    if (userInfo?.id) {
      // 如果storage中有但globalData中没有，同步到globalData
      if (!app.globalData.userInfo) {
        app.globalData.userInfo = userInfo
      }
      return userInfo.id
    }
    
    return null
  }
  
  // 销毁实例
  destroy() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }
    
    this.messageHandlers.clear()
    this.roomSubscriptions.clear()
    this.connected = false
    
    WebSocketManager.instance = null
    console.log('WebSocket管理器已销毁')
  }
}

module.exports = WebSocketManager
